package com.cancer.common.util;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean.MethodInvokingJob;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import com.cancer.common.web.ApplicationContextUtils;


/**
 * 定时任务工具类
 * 
 * @company GeekPlus
 * @project beetle
 * @author 刘俊
 * @date 2016年11月25日
 * @since 1.0.0
 */
public class SpringQuartzUtils {
    private static Logger logger = LoggerFactory.getLogger(SpringQuartzUtils.class);
    private static Scheduler scheduler;

    public static synchronized Scheduler getScheduler() {
        if (scheduler == null) {
            scheduler = ApplicationContextUtils.getBean(SchedulerFactoryBean.class).getScheduler();
        }
        return scheduler;
    }

    public static void setScheduler(Scheduler scheduler) {
        SpringQuartzUtils.scheduler = scheduler;
    }

    public static TriggerKey getTriggerKey(String triggerCode) {
        return TriggerKey.triggerKey(triggerCode, Scheduler.DEFAULT_GROUP);
    }

    /**
     * 配置任务计划
     * 
     * @author guojianxia 2016年9月1日
     * @param triggerBean
     * @return
     */
    public static boolean updateCronTrigger(QuartzCronTriggerBean triggerBean) {
        boolean result = false;

        try {
            TriggerKey triggerKey = getTriggerKey(triggerBean.getTriggerCode());
            CronTriggerImpl trigger = (CronTriggerImpl) getScheduler().getTrigger(triggerKey);
            // 如果计划任务已存在则调用修改方法
            if (trigger != null) {
                // 判断任务是否可用
                if (checkTriggerBean(triggerBean)) {
                    // 更新cron定时表达式
                    if (!trigger.getCronExpression().equalsIgnoreCase(triggerBean.getTriggerExpression())) {
                        logger.info("更新{}计划任务, 原值: {}, 新值: {}", triggerBean.getTriggerCode(), trigger.getCronExpression(), triggerBean.getTriggerExpression());
                        trigger.setCronExpression(triggerBean.getTriggerExpression());
                        trigger.setStartTime(new Date());
                        getScheduler().rescheduleJob(triggerKey, trigger);
                    }

                    // 更新job属性
                    // 注意：如果不加&，获取到的将是JobDetail的一个实现，而不是MethodInvokingJobDetailFactoryBean
                    MethodInvokingJobDetailFactoryBean mjdfb = ApplicationContextUtils.getBean("&" + triggerBean.getJobDetailName());
                    if (mjdfb != null) {
                        mjdfb.setName(triggerBean.getJobDetailName());
                        // Class<?> jobClass = (this.concurrent ? MethodInvokingJob.class :
                        // StatefulMethodInvokingJob.class);
                        boolean needReschedule = false;
                        // boolean concurrent = jobImpl.getJobClass().equals(MethodInvokingJob.class) ?
                        // true : false;
                        JobDetailImpl jobImpl = (JobDetailImpl) getScheduler().getJobDetail(trigger.getJobKey());
                        boolean concurrent = jobImpl.getJobClass().equals(MethodInvokingJob.class) ? true : false;
                        if (concurrent != triggerBean.isConcurrent()) {
                            mjdfb.setConcurrent(triggerBean.isConcurrent());
                            // needReschedule = true; // TODO 暂不支持修改是否允许并发
                        }
                        if (!mjdfb.getTargetMethod().equals(triggerBean.getTargetMethodName())) {
                            mjdfb.setTargetMethod(triggerBean.getTargetMethodName());
                            needReschedule = true;
                        }
                        if (!mjdfb.getTargetClass().getName().equals(triggerBean.getTargetClassName())) {
                            mjdfb.setTargetObject(Class.forName(triggerBean.getTargetClassName()).newInstance());
                            needReschedule = true;
                        }
                        if (needReschedule) {
                            // 停止触发器，移除触发器，删除任务
                            getScheduler().pauseTrigger(triggerKey);
                            getScheduler().unscheduleJob(triggerKey);
                            getScheduler().deleteJob(trigger.getJobKey());
                            createCronTrigger(triggerBean, triggerKey);
                        }
                    }

                } else {
                    // 停止触发器，移除触发器，删除任务
                    getScheduler().pauseTrigger(triggerKey);
                    getScheduler().unscheduleJob(triggerKey);
                    getScheduler().deleteJob(trigger.getJobKey());
                    logger.info("删除{}计划任务。", triggerBean.getTriggerCode());
                }
            } else {
                // 如果计划任务不存在并且数据库里的任务状态为可用时,则创建计划任务
                if (checkTriggerBean(triggerBean)) {
                    createCronTrigger(triggerBean, triggerKey);
                }
            }

            result = true;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("配置任务计划失败！产生异常：{}", e);
        }

        return result;
    }

    /**
     * 判断调度计划时候可以创建或更新
     * 
     * @author wangteng 2017年12月5日下午7:53:50
     * @param waveStrategyType
     * @return
     */
    private static Boolean checkTriggerBean(QuartzCronTriggerBean triggerBean) {
        SimpleDateFormat formatyMd = new SimpleDateFormat("yyyy-MM-dd");
        Long now = System.currentTimeMillis();
        if (triggerBean.getStatus() == null) {
            logger.info("调度计划参数有误，跳过创建或更新");
            return false;
        }

        if (triggerBean.getRunStatus() == null) {
            logger.info("调度计划参数有误，跳过创建或更新");
            return false;
        }

        if (triggerBean.getExecuteDateBegin() == null || triggerBean.getExecuteDateEnd() == null) {
            logger.info("调度计划参数有误，跳过创建或更新");
            return false;
        }

        if (!StringUtil.isNotEmpty(triggerBean.getExecuteTimeBegin()) || !StringUtil.isNotEmpty(triggerBean.getExecuteTimeEnd())) {
            logger.info("调度计划参数有误，跳过创建或更新");
            return false;
        }

        if (triggerBean.getStatus().compareTo(0) == 0) {
            logger.info("非启用状态，跳过创建或更新");
            return false;
        }

        if (triggerBean.getRunStatus().compareTo(0) == 0) {
            logger.info("非运行状态，跳过创建或更新");
            return false;
        }

        if (triggerBean.getExecuteDateBegin() != 0 && triggerBean.getExecuteDateEnd() != 0) {
            if (now < triggerBean.getExecuteDateBegin() || now > triggerBean.getExecuteDateEnd()) {
                logger.info("不在日期范围:{}至{}，跳过创建或更新", formatyMd.format(new Date(triggerBean.getExecuteDateBegin())), formatyMd.format(new Date(triggerBean.getExecuteDateEnd())));
                return false;
            }
        }

        if (!"0".equals(triggerBean.getExecuteTimeBegin()) && !"0".equals(triggerBean.getExecuteTimeEnd())) {
            String now_str = formatyMd.format(new Date());
            SimpleDateFormat format24 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            String timeBegin = now_str + " " + triggerBean.getExecuteTimeBegin() + ":00";
            String timeEnd = now_str + " " + triggerBean.getExecuteTimeEnd() + ":59";

            try {
                long timeBegin_long = format24.parse(timeBegin).getTime();
                long timeEnd_long = format24.parse(timeEnd).getTime();
                if (now < timeBegin_long || now > timeEnd_long) {
                    logger.info("不在时间范围:{}至{}，跳过创建或更新", timeBegin, timeEnd);
                    return false;
                }
            } catch (Exception e) {
                logger.error("时间转换出错，跳过创建或更新");
                return false;
            }
        }

        return true;
    }

    /**
     * 创建或添加新的计划任务
     * 
     * @param triggerBean
     *            计划任务配置对象
     * @throws Exception
     */
    public static void createCronTrigger(QuartzCronTriggerBean triggerBean, TriggerKey triggerKey) throws Exception {
        // 新建一个基于Spring的管理Job类
        MethodInvokingJobDetailFactoryBean mjdfb = new MethodInvokingJobDetailFactoryBean();
        // 设置Job名称
        mjdfb.setName(triggerBean.getJobDetailName());
        // 根据任务类是否为Spring定义的Bean来设置任务类
        if (triggerBean.isSpringBean()) {
            // Object obj =
            // ApplicationContextUtils.getBean(triggerBean.getTargetClassName());
            Object obj = ApplicationContextUtils.getBean(Class.forName(triggerBean.getTargetClassName()));
            mjdfb.setTargetObject(obj != null ? obj : Class.forName(triggerBean.getTargetClassName()).newInstance());
        } else {
            mjdfb.setTargetObject(Class.forName(triggerBean.getTargetClassName()).newInstance());
        }
        // 设置任务方法
        mjdfb.setTargetMethod(triggerBean.getTargetMethodName());
        // 设置是否并发启动任务
        mjdfb.setConcurrent(triggerBean.isConcurrent());
        // 将管理Job类提交到计划管理类
        mjdfb.afterPropertiesSet();
        // 将Spring的管理Job类转为Quartz管理Job类
        JobDetailImpl jobDetail = new JobDetailImpl();
        jobDetail = (JobDetailImpl) mjdfb.getObject();
        jobDetail.setName(triggerBean.getJobDetailName());

        // 将Job添加到管理类
        getScheduler().addJob(jobDetail, true);
        // 新一个基于Spring的时间类
        CronTriggerFactoryBean c = new CronTriggerFactoryBean();
        c.setCronExpression(triggerBean.getTriggerExpression());
        c.setName(triggerBean.getTriggerCode());
        // 注入Job
        c.setJobDetail(jobDetail);
        c.afterPropertiesSet();
        // 注入到管理类
        CronTrigger trigger = c.getObject();
        ((CronTriggerImpl) trigger).setStartTime(new Date());
        getScheduler().scheduleJob(trigger);

        // 刷新管理类
        getScheduler().rescheduleJob(triggerKey, trigger);
        logger.info("新建" + triggerBean.getTriggerCode() + "计划任务");
    }

    /**
     * 暂停一个job
     * 
     * @author wangteng 2017年11月23日下午2:16:30
     * @param triggerBean
     * @throws SchedulerException
     */
    public static void pauseJob(QuartzCronTriggerBean triggerBean) {
        try {
            TriggerKey triggerKey = getTriggerKey(triggerBean.getTriggerCode());
            CronTriggerImpl trigger = (CronTriggerImpl) getScheduler().getTrigger(triggerKey);
            if (trigger != null) {
                getScheduler().pauseTrigger(triggerKey);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("暂停任务计划失败！产生异常：{}", e);
        }

    }

    /**
     * 恢复一个job
     * 
     * @author wangteng 2017年11月23日下午2:16:22
     * @param triggerBean
     * @throws SchedulerException
     */
    public static void resumeJob(QuartzCronTriggerBean triggerBean) {
        try {
            TriggerKey triggerKey = getTriggerKey(triggerBean.getTriggerCode());
            CronTriggerImpl trigger = (CronTriggerImpl) getScheduler().getTrigger(triggerKey);
            if (trigger != null) {
                getScheduler().resumeTrigger(triggerKey);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
            logger.error("回复任务计划失败！产生异常：{}", e);
        }
    }

    /**
     * 立即执行job
     * 
     * @author wangteng 2017年11月23日上午11:43:36
     * @param triggerBean
     * @throws SchedulerException
     */
    public static void runAJobNow(QuartzCronTriggerBean triggerBean) {
        try {
            TriggerKey triggerKey = getTriggerKey(triggerBean.getTriggerCode());
            CronTriggerImpl trigger = (CronTriggerImpl) getScheduler().getTrigger(triggerKey);
            if (trigger != null) {
                getScheduler().triggerJob(trigger.getJobKey());
            }
            logger.info("立即执行{}计划任务。", triggerBean.getTriggerCode());
        } catch (SchedulerException e) {
            e.printStackTrace();
            logger.error("立即执行任务计划失败！产生异常：{}", e);
            throw new IllegalStateException("立即执行任务计划失败！");
        }
    }

    public static interface QuartzCronTriggerBean {
        /** 如果计划任务不存在并且数据库里的任务状态为可用时,则创建计划任务 1为不存在 */
        public Integer getStatus();

        /** 触发器运行状态 */
        public Integer getRunStatus();

        /** 触发器名称 */
        public String getTriggerCode();

        /** 触发器执行开始日期 */
        public Long getExecuteDateBegin();

        /** 触发器执行结束日期 */
        public Long getExecuteDateEnd();

        /** 触发器开始执行时间 */
        public String getExecuteTimeBegin();

        /** 触发器结束执行时间 */
        public String getExecuteTimeEnd();

        /** 触发器cron表达式 */
        public String getTriggerExpression();

        /** 触发器引用的job */
        public String getJobDetailName();

        /** 任务类名 */
        public String getTargetClassName();

        /** 类名对应的方法名 */
        public String getTargetMethodName();

        /** 是否允许并发启动任务 */
        public boolean isConcurrent();

        /** 是否允许是已经存在的bean */
        public boolean isSpringBean();
    }
}
